﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Shell;
using ParallelUtils;
using RayTracer;

namespace Demo
{
    public partial class MainWindow : Window
    {
        public const int chunkSize = 64;

        public Dictionary<string, Size> Sizes { get; private set; }

        private Tessellator tessellator;
        private Scene scene;

        public MainWindow()
        {
            InitializeComponent();

            Sizes = new Dictionary<string, Size>()          
            {                
                {"QQVGA (160×120)", new Size(160,120)},
                {"HQVGA (240×160)", new Size(240,160)},
                {"QVGA (320×240)", new Size(320,240)},
                {"WQVGA (400×240)", new Size(400,240)},
                {"HVGA (480×320)", new Size(480,320)},
                {"VGA (640×480)", new Size(640,480)},
                {"WVGA (800×480)", new Size(800,480)},
                {"FWVGA (854×480)", new Size(854,480)},
                {"SVGA (800×600)", new Size(800,600)},
                {"DVGA (960×640)", new Size(960,640)},
                {"WSVGA (1024×576)", new Size(1024,576)},
                {"XGA (1024×768)", new Size(1024,768)},
                {"WXGA (1280×768)", new Size(1280,768)},
                {"XGA+ (1152×864)", new Size(1152,864)},
                {"WXGA+ (1440×900)", new Size(1440,900)},
                {"SXGA (1280×1024)", new Size(1280,1024)},
                {"SXGA+ (1400×1050)", new Size(1400,1050)},
                {"WSXGA+ (1680×1050)", new Size(1680,1050)},
                {"UXGA (1600×1200)", new Size(1600,1200)},
                {"WUXGA (1920×1200)", new Size(1920,1200)},
                {"QWXGA (2048×1152)", new Size(2048,1152)},
                {"QXGA (2048×1536)", new Size(2048,1536)},
                {"WQXGA (2560×1600)", new Size(2560,1600)},
                {"QSXGA (2560×2048)", new Size(2560,2048)},
                {"WQSXGA (3200×2048)", new Size(3200,2048)},
                {"QUXGA (3200×2400)", new Size(3200,2400)},
                {"WQUXGA (3840×2400)", new Size(3840,2400)},
                {"HXGA (4096×3072)", new Size(4096,3072)},
                {"WHXGA (5120×3200)", new Size(5120,3200)},
                {"HSXGA (5120×4096)", new Size(5120,4096)},
                {"WHSXGA (6400×4096)", new Size(6400,4096)},
                {"HUXGA (6400×4800)", new Size(6400,4800)},
                {"WHUXGA (7680×4800)", new Size(7680,4800)},
                {"nHD (640×360)", new Size(640,360)},
                {"qHD (960×540)", new Size(960,540)},
                {"HD (1280×720)", new Size(1280,720)},
                {"FHD (1920×1080)", new Size(1920,1080)},
                {"WQHD (2560×1440)", new Size(2560,1440)},
                {"QFHD (3840×2160)", new Size(3840,2160)},
                {"UHD (7680×4320)", new Size(7680,4320)}
                };

            scene = SceneFactory.Mollecules();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            comboSize.ItemsSource = Sizes.Keys;
            comboSize.SelectedIndex = 6;
        }

        private void ComboSize_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var size = Sizes[(string)comboSize.SelectedValue];
            textSize.Text = (string)comboSize.SelectedValue;

            var sWidth = Math.Min(size.Width, SystemParameters.PrimaryScreenWidth * .8);
            var sHeight = sWidth * size.Height / size.Width;

            Width = sWidth + 20;
            Height = sHeight + 64;

            scene.Camera.Sensor = new Sensor(.035, (int)size.Width, (int)size.Height);

            tessellator = new Tessellator((int)size.Width, (int)size.Height, chunkSize);
            tessellator.ProcessChunk += Tessellator_ProcessChunk;
            tessellator.ProgressChanged += Tessellator_ProgressChanged;
            tessellator.Completed += Tessellator_Completed;
            tessellator.ShuffleSequence();

            imgMain.Source = tessellator.Bitmap;

            Start();
        }
        private void ButtonStart_Click(object sender, RoutedEventArgs e)
        {
            Start();
        }
        private void ButtonSave_Click(object sender, RoutedEventArgs e)
        {
            tessellator.ExportPNG("image.png");
            MessageBox.Show(this, "Image saved to \"image.png\"");
        }
        private void ButtonStop_Click(object sender, RoutedEventArgs e)
        {
            UpdateScreen(() =>
            {
                txtProgress.Text = "Stopping...";
            });

            tessellator.RequestStop();
        }
        private void Window_MouseMove(object sender, MouseEventArgs e)
        {
            menu.Visibility = menu.IsMouseOver || e.GetPosition(this).Y <= 20 ? Visibility.Visible : Visibility.Hidden;
        }

        private void Tessellator_ProcessChunk(BitmapChunk chunk)
        {
            for (int y = 0; y < chunk.Height; y++)
                for (int x = 0; x < chunk.Width; x++)
                    chunk.DrawPixel(x, y, scene.GetPixelColor(x + chunk.X, y + chunk.Y).ToWMColor());
        }
        private void Tessellator_ProgressChanged(int pct)
        {
            UpdateScreen(() =>
            {
                if (!tessellator.IsStopRequested)
                    txtProgress.Text = (pct.ToString() + "%");
                barProgress.Value = pct;
                TaskbarItemInfo.ProgressValue = 1.0 * pct / 100;
            });
        }
        private void Tessellator_Completed(int ms)
        {
            UpdateScreen(() =>
            {
                txtProgress.Text = ms.ToString("0ms");
            });

            FixVisibility(false);
        }

        private void Start()
        {
            FixVisibility(true);

            tessellator.ClearBitmap();
            tessellator.BackgroundRun();
        }
        private void FixVisibility(bool isRunning)
        {
            UpdateScreen(() =>
            {
                Cursor = isRunning ? Cursors.Wait : Cursors.Arrow;

                stackStart.IsEnabled = !isRunning;
                stackStop.IsEnabled = isRunning;

                TaskbarItemInfo.ProgressState = isRunning ? TaskbarItemProgressState.Normal : TaskbarItemProgressState.None;
                if (!isRunning)
                    barProgress.Value = 0;
            });
        }
        private void UpdateScreen(Action updateAction)
        {
            this.Dispatcher.Invoke(updateAction);
        }
    }
}
